/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./vXmlInputStream.h"

namespace Vendetta {
	vBool vXmlInputStream::LoadFromFile(const vString& fileName) {
		if (!mDocTag.LoadFile(fileName.ToConstCharPtr())) {
			return false;
		}

		return postLoad();
	}

	vInt vXmlInputStream::attributeInt(const vString& attrName) {
		vString str = attributeString(attrName);
		vBool bOK = false;
		vInt iVal = vStringConverter::ToInt(str, &bOK);
		if (!bOK) {
			throw vException("Attribute \"%s\" is not integer. In vXmlInputStream::attributeInt.", 
				attrName.ToConstCharPtr());
		}

		return iVal;	
	}

	vReal vXmlInputStream::attributeReal(const vString& attrName) {
		vString str = attributeString(attrName);			
		vBool bOK = false;
		vReal rVal = vStringConverter::ToReal(str, &bOK);
		if (!bOK) {
			throw vException("Attribute \"%s\" is not real. In vXmlInputStream::attributeReal.", 
				attrName.ToConstCharPtr());
		}

		return rVal;
	}

	vBool vXmlInputStream::attributeBool(const vString& attrName) {
		vString str = attributeString(attrName);			
		vBool bOK = false;
		vBool bVal = vStringConverter::ToBool(str, &bOK);
		if (!bOK) {
			throw vException("Attribute \"%s\" is not bool. In vXmlInputStream::attributeBool.", 
				attrName.ToConstCharPtr());
		}

		return bVal;
	}

	vUInt vXmlInputStream::attributeUInt(const vString& attrName) {
		vString str = attributeString(attrName);			
		vBool bOK = false;
		vUInt uiVal = vStringConverter::ToUInt(str, &bOK);
		if (!bOK) {
			throw vException("Attribute \"%s\" is not unsigned int. In vXmlInputStream::attributeUInt.", 
				attrName.ToConstCharPtr());
		}

		return uiVal;
	}

	vUInt32 vXmlInputStream::attributeUInt32(const vString& attrName) {
		vString str = attributeString(attrName);			
		vBool bOK = false;
		vUInt32 uiVal = vStringConverter::ToUInt32(str, &bOK);
		if (!bOK) {
			throw vException("Attribute \"%s\" is not unsigned long. In vXmlInputStream::attributeUInt32.", 
				attrName.ToConstCharPtr());
		}

		return uiVal;
	}

	vLong vXmlInputStream::attributeLong(const vString& attrName) {
		vString str = attributeString(attrName);			
		vBool bOK = false;
		vLong uiVal = vStringConverter::ToLong(str, &bOK);
		if (!bOK) {
			throw vException("Attribute \"%s\" is not long. In vXmlInputStream::attributeLong.", 
				attrName.ToConstCharPtr());
		}

		return uiVal;
	}

	vString vXmlInputStream::attributeString(const vString& attrName) {
		if (mTags.IsEmpty()) {
			throw vException("Attribute \"%s\" does not exists in current path. In vXmlInputStream::attributeString.", 
				attrName.ToConstCharPtr());
		}
		TiXmlElement* pElem = mTags.Top();
		if (pElem) {
			return vString(pElem->Attribute(attrName.ToConstCharPtr()));
		} else {
			throw vException("Attribute \"%s\" does not exists in current path. In vXmlInputStream::attributeString.", 
				attrName.ToConstCharPtr());
		}
	}

	vString vXmlInputStream::textTag(const vString& tagName) {
		if (mTags.IsEmpty()) {
			throw vException("Tag \"%s\" does not exists in current path. In vXmlInputStream::textTag.", 
				tagName.ToConstCharPtr());
		}
		TiXmlElement* pElem = mTags.Top();
		if (pElem) {
			if (vString("").IsEqual(tagName)) {
				const char* txt = pElem->GetText();
				if (txt) {
					return vString(txt);
				} else {
					throw vException("Tag \"%s\" does not contain text node. In vXmlInputStream::textTag.", 
						pElem->Value());
				}
			} else {
				if (pElem->FirstChild(tagName.ToConstCharPtr())) {
					pElem = pElem->FirstChild(tagName.ToConstCharPtr())->ToElement();
					if (pElem) {
						return vString(pElem->GetText());
					}
				}
				throw vException("Tag \"%s\" does not exists in current path. In vXmlInputStream::textTag.", 
					tagName.ToConstCharPtr());
			}
		} else {
			throw vException("Tag does not exists in current path. In vXmlInputStream::textTag.");
		}
	}

	vString vXmlInputStream::tagName() {
		if (!mTags.IsEmpty()) {
			TiXmlElement* pElem = mTags.Top();
			if (pElem) {
				return vString(pElem->Value());
			}
		}

		return vString("");
	}

	vVoid vXmlInputStream::openTagForRead(const vString& path) {
		try {
			if (path.Length() > 0 && !path.SubString(0, 1).IsEqual("/")) {
				mTags.Clear();
			}
			traversePath(path);
		} catch(vException& e) {
			throw vException("Can't open tag because of: %s In vXmlInputStream::openTag.", 
				e.ErrorMsg().ToConstCharPtr());
		}
	}

	vVoid vXmlInputStream::closeLastOpenTagForRead() {
		if (!mTags.IsEmpty()) {
			mTags.Pop();
		}
	}

	vVoid vXmlInputStream::closeAllOpenTagsForRead() {
		mTags.Clear();
	}

	vBool vXmlInputStream::iterateChildTags() {
		if (!mTags.IsEmpty()) {
			TiXmlElement* pElem = mTags.Top();
			TiXmlNode* pChild = pElem->IterateChildren(vNull);
			if (pChild && pChild->ToElement()) {
				mTags.Push(pChild->ToElement());

				return true;
			} 
		}
		return false;
	}

	vBool vXmlInputStream::nextChildTag() {
		if (!mTags.IsEmpty()) {
			TiXmlElement* pChild = mTags.Top();
			mTags.Pop();
			TiXmlElement* pElem = mTags.Top();
			TiXmlNode* pNewChild = pElem->IterateChildren(pChild);
			if (pNewChild && pNewChild->ToElement()) {
				mTags.Push(pNewChild->ToElement());

				return true;
			}
		}

		return false;
	}

	vBool vXmlInputStream::iterateSiblingTags() {
		if (!mTags.IsEmpty()) {
			TiXmlElement* pElem = mTags.Top();
			TiXmlElement* pSibling = pElem->NextSiblingElement();
			if (pSibling) {
				return true;
			} 
		}
		return false;
	}

	vBool vXmlInputStream::nextSiblingTag() {
		if (!mTags.IsEmpty()) {
			TiXmlElement* pElem = mTags.Top();
			TiXmlElement* pSibling = pElem->NextSiblingElement();
			if (pSibling) {
				mTags.Push(pSibling);

				return true;
			} 
		}
		return false;
	}

	vVoid vXmlInputStream::traversePath(const vString& path) {
		vArray<vString> list = vStringTokenizer::Split(vStringTokenizer::EraseDelimeter(path, " "), "/");
		vUInt uiIndex = 0, uiElems;
		
		while (uiIndex < list.Size()) {
			if (list[uiIndex].IsEqual(".")) {
				list.Remove(uiIndex);
			} else {
				uiIndex++;
			}
		}

		if (list.Size() > 0) {
			TiXmlElement* pElem = vNull;
			TiXmlHandle hParent(vNull);

			pElem = rootTag(list[0]);
			if (!pElem) {
				throw vException("Can't find \"%s\" node. In vXmlInputStream::traversePath.", 
					list[0].ToConstCharPtr());
			}
			uiElems = mTags.IsEmpty() ? 1 : 0;
			mTags.Push(pElem);

			while (uiElems < list.Size()) {
				if (list[uiElems].IsEqual("..")) {
					mTags.Pop().Pop();
					pElem = openParentTag(pElem);
				} else {
					pElem = openChildTag(pElem, list[uiElems]);
				}
				if (pElem) {
					mTags.Push(pElem);
					uiElems++;
				} else {
					mTags.Clear();
					throw vException("Can't find \"%s\" node from \"%s\" path. In vXmlInputStream::traversePath.", 
						list[uiElems].ToConstCharPtr(), path.ToConstCharPtr());
				}
			}
		}
	}

	TiXmlElement* vXmlInputStream::rootTag(const vString& rootTag) {
		if (mTags.IsEmpty()) {
			TiXmlHandle hDoc(&mDocTag);

			return hDoc.FirstChildElement(rootTag.ToConstCharPtr()).Element();
		} else {
			return mTags.Top();
		}
	}

	TiXmlElement* vXmlInputStream::openParentTag(TiXmlElement* pElem) {
		if (pElem) {
			TiXmlNode* par = pElem->Parent();
			if (par) {
				TiXmlElement* elem = par->ToElement();
				if (elem) {
					return elem;
				}
			}
		}

		return vNull;
	}

	TiXmlElement* vXmlInputStream::openChildTag(TiXmlElement* pParent, const vString& childName) {
		if (pParent) {
			return pParent->FirstChildElement(childName.ToConstCharPtr());
		}

		return vNull;
	}
}